package glemipsc.implementations.glemips32;

import java.util.ArrayList;
import java.util.List;

import glemipsc.Address;
import glemipsc.implementations.Argument;
import glemipsc.implementations.Instruction;

public class Implementation extends glemipsc.implementations.Implementation {
	private Register[] regs = new Register[32];
	
	public Implementation() {
		for (int i = 0; i < 32; i++) {
			regs[i] = new Register(i);
		}
	}
	
	public Register getRegister(int n) {
		return regs[n];
	}

	@Override
	public int getCharSize() {
		//return 8;
		return 32;
	}

	@Override
	public int getDoubleSize() {
		return 32;
	}

	@Override
	public int getFloatSize() {
		//return 16;
		return 32;
	}

	@Override
	public int getLongIntegerSize() {
		return 32;
	}

	@Override
	public int getPointerSize() {
		//return 16;
		return 32;
	}

	@Override
	public int getShortIntegerSize() {
		//return 16;
		return 32;
	}

	@Override
	public boolean isDefaultShort() {
		return true;
	}

	@Override
	public int getNumberOfRegisters() {
		return 32;
	}

	@Override
	public int getRegistersBits() {
		return 32;
	}

	@Override
	public int getReservedRegisters() {
		// R31: Return Address
		// R30: Stack Pointer
		return 2;
	}

	public Argument getReturnRegister() {
		return regs[31];
	}
	
	@Override
	public Argument getStackPointerRegister() {
		return regs[30];
	}
	
	@Override
	public Argument getZeroRegister() {
		return regs[0];
	}

	@Override
	public int getMaximumImmediateBits() {
		return 16;
	}

	@Override
	public void freeRegister(Argument arg) {
		Register reg = (Register) arg;
		reg.setBusy(false);
	}

	@Override
	public Argument getFreeRegister() {
		for (int i = 1; i < getNumberOfRegisters()-getReservedRegisters(); i++) {
			if (!regs[i].isBusy()) {
				regs[i].setBusy(true);
				return regs[i];
			}
		}
		return null;
	}

	@Override
	public Instruction getMov(Argument dest, Argument source) {
		return new Mov(dest, source);
	}

	@Override
	public Instruction getAdd(Argument arg1, Argument arg2, Argument arg3, boolean signed) {
		return new Add(arg1, arg2, arg3, signed);
	}

	@Override
	public Instruction getAnd(Argument arg1, Argument arg2, Argument arg3) {
		return new And(arg1, arg2, arg3);
	}

	@Override
	public Instruction getDiv(Argument arg1, Argument arg2, Argument arg3, boolean signed) {
		return new Div(arg1, arg2, arg3, signed);
	}

	@Override
	public Instruction getJgez(Argument arg1, Argument arg2) {
		return new Jgez(arg1, arg2);
	}

	@Override
	public Instruction getJgz(Argument arg1, Argument arg2) {
		return new Jgz(arg1, arg2);
	}

	@Override
	public Instruction getJlez(Argument arg1, Argument arg2) {
		return new Jlez(arg1, arg2);
	}

	@Override
	public Instruction getJlz(Argument arg1, Argument arg2) {
		return new Jlz(arg1, arg2);
	}

	@Override
	public Instruction getJmp(Argument arg) {
		return new Jmp(arg);
	}

	@Override
	public Instruction getJnz(Argument arg1, Argument arg2) {
		return new Jnz(arg1, arg2);
	}

	@Override
	public Instruction getJz(Argument arg1, Argument arg2) {
		return new Jz(arg1, arg2);
	}

	@Override
	public Instruction getLls(Argument arg1, Argument arg2, Argument arg3) {
		return new Lls(arg1, arg2, arg3);
	}

	@Override
	public Instruction getLrs(Argument arg1, Argument arg2, Argument arg3) {
		return new Lrs(arg1, arg2, arg3);
	}

	@Override
	public Instruction getMul(Argument arg1, Argument arg2, Argument arg3, boolean signed) {
		return new Mul(arg1, arg2, arg3, signed);
	}

	@Override
	public Instruction getOr(Argument arg1, Argument arg2, Argument arg3) {
		return new Or(arg1, arg2, arg3);
	}

	@Override
	public Instruction getSub(Argument arg1, Argument arg2, Argument arg3, boolean signed) {
		return new Sub(arg1, arg2, arg3, signed);
	}

	@Override
	public Instruction getXor(Argument arg1, Argument arg2, Argument arg3) {
		return new Xor(arg1, arg2, arg3);
	}

	@Override
	public Argument getImmediate(int n) {
		return new Immediate(n);
	}

	@Override
	public Instruction getLd(Argument arg, Address addr, int size) {
		return new Ld(arg, addr, size);
	}

	@Override
	public Instruction getSt(Argument arg, Address addr, int size) {
		return new St(arg, addr, size);
	}

	@Override
	public Instruction getJal(Argument arg1) {
		return new Jal(arg1);
	}

	@Override
	public List<Argument> getBusyRegisters() {
		ArrayList<Argument> regs = new ArrayList<Argument>();
		for (int i = 1; i < this.getNumberOfRegisters()-this.getReservedRegisters(); i++) {
			if (this.regs[i].isBusy())
				regs.add(this.regs[i]);
		}
		return regs;
	}

	@Override
	public Instruction getNop() {
		return new Nop();
	}

	@Override
	public Argument[] getRegisters() {
		return regs;
	}

}
